using System;
using System.Collections.Generic;
using System.Linq;
using Gj;
using Unity.Burst;
using Unity.Burst.Intrinsics;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Transforms;
using UnityEngine;

[WorldSystemFilter(WorldSystemFilterFlags.Disabled)]
[UpdateAfter(typeof(AiExecSystem))]
public partial class AiSystem : SystemBase
{
    public BeginInitializationEntityCommandBufferSystem CommandBufferSystem;

    private NativeArray<Entity> GroupDataFromGroup;
    private EntityQuery judgeGroup;
    private NativeList<Entity> NewGroupList;

    private EntityQuery oldGroup;

    private EntityQuery reScanGroup;
    private EntityQuery runStateGroup;
    private EntityQuery startStateGroup;
    private EntityQuery updateScanGroup;
    private EntityQuery waitGroupGroup;
    private EntityQuery waitScanGroup;

    protected override void OnCreate()
    {
        CommandBufferSystem = World.GetOrCreateSystemManaged<BeginInitializationEntityCommandBufferSystem>();

        NewGroupList = new NativeList<Entity>(Allocator.Persistent);

        // 找出所有组
        oldGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiGroupData>()
            }
        });

        // 初始化组队信息
        waitGroupGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiData>(),
                ComponentType.ReadOnly<EntityData>(),
                ComponentType.ReadOnly<AiWaitGroupData>()
            }
        });

        // 根据碰撞器获取扫描对象
        waitScanGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiData>(),
                ComponentType.ReadOnly<AiGroupData>(),
                ComponentType.ReadOnly<LocalTransform>()
            },
            None = new[]
            {
                ComponentType.ReadWrite<CastColliderData>(),
                ComponentType.ReadWrite<CastHitsData>()
            }
        });

        reScanGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiData>(),
                ComponentType.ReadOnly<AiGroupData>(),
                ComponentType.ReadOnly<LocalTransform>(),
                ComponentType.ReadOnly<HitData>()
            },
            None = new[]
            {
                ComponentType.ReadWrite<CastColliderData>(),
                ComponentType.ReadWrite<CastHitsData>()
            }
        });

        updateScanGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiData>(),
                ComponentType.ReadOnly<EntityData>(),
                ComponentType.ReadWrite<AiGroupData>(),
                ComponentType.ReadOnly<LocalTransform>(),
                ComponentType.ReadOnly<CastHitsData>()
            }
        });
        // 得到碰撞结果，开启判断
        startStateGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiData>(),
                ComponentType.ReadOnly<AiBrainStateData>()
            },
            None = new[]
            {
                ComponentType.ReadWrite<AiWaitGroupData>()
            }
        });

        // 进行判断
        judgeGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiData>(),
                ComponentType.ReadWrite<AiBrainStateData>(),
                ComponentType.ReadOnly<EntityData>(),
                ComponentType.ReadOnly<LocalTransform>(),
                ComponentType.ReadOnly<AiSelectData>(),
                ComponentType.ReadOnly<AiLastCommandData>()
            },
            None = new[]
            {
                ComponentType.ReadWrite<AiLockStateData>()
            }
        });

        // 运行状态处理
        runStateGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AiData>(),
                ComponentType.ReadOnly<AiLastCommandData>(),
                ComponentType.ReadWrite<AiBrainStateData>(),
                ComponentType.ReadOnly<LocalTransform>()
            },
            None = new[]
            {
                ComponentType.ReadWrite<AiLockStateData>()
            }
        });
    }

    protected override void OnDestroy()
    {
        base.OnDestroy();
        NewGroupList.Dispose();
    }

    protected override void OnUpdate()
    {
        var inputDeps = Dependency;
        var time = SystemAPI.Time.ElapsedTime;

        var commandBuffer = CommandBufferSystem.CreateCommandBuffer().AsParallelWriter();
        var AiDataType = GetComponentTypeHandle<AiData>();
        var EntityDataType = GetComponentTypeHandle<EntityData>();
        var EntityType = GetEntityTypeHandle();
        var AiSelectDataType = GetComponentTypeHandle<AiSelectData>();
        var AiLastCommandDataType = GetComponentTypeHandle<AiLastCommandData>();
        var TranslationType = GetComponentTypeHandle<LocalTransform>();
        var HitDataType = GetComponentTypeHandle<HitData>();

        var AiDataFromEntity = GetComponentLookup<AiData>();
        var AiGroupDataFromEntity = GetComponentLookup<AiGroupData>();
        var CastHitsDataType = GetBufferLookup<CastHitsData>();
        var EntityDataFromEntity = GetComponentLookup<EntityData>();
        var TranslationFromEntity = GetComponentLookup<LocalTransform>();
        var TeamDataFromEntity = GetComponentLookup<AiTeamData>();
        var TargetBufferFromEntity = GetBufferLookup<AiTargetBuffer>();
        var EnemyBufferFromEntity = GetBufferLookup<AiEnemyBuffer>();
        var PartnerBufferFromEntity = GetBufferLookup<AiPartnerBuffer>();

        JobHandle TeamDataJob;
        GroupDataFromGroup = oldGroup.ToEntityArrayAsync(Allocator.TempJob, out TeamDataJob);

        NewGroupList.Resize(waitGroupGroup.CalculateEntityCount(), NativeArrayOptions.ClearMemory);
        NewGroupList.Clear();
        var waitGroupJob = new WaitGroupJob
        {
            CommandBuffer = commandBuffer,
            NewGroupList = NewGroupList.AsParallelWriter(),
            AiDataType = AiDataType,
            EntityType = EntityType,
            EntityDataType = EntityDataType,
            TranslationType = TranslationType,
            GroupDataFromGroup = GroupDataFromGroup,
            TranslationFromEntity = TranslationFromEntity,
            EntityDataFromEntity = EntityDataFromEntity
        };
        waitGroupJob.Schedule(waitGroupGroup, TeamDataJob).Complete();
        GroupDataFromGroup.Dispose();

        var newGroupJob = new NewGroupJob
        {
            CommandBuffer = commandBuffer,
            NewGroupList = NewGroupList.AsDeferredJobArray(),
            AiDataFromEntity = AiDataFromEntity,
            EntityDataFromEntity = EntityDataFromEntity,
            TranslationFromEntity = TranslationFromEntity
        };
        inputDeps = newGroupJob.Schedule(NewGroupList.Length, 1);

        var scanJob = new ScanJob
        {
            CommandBuffer = commandBuffer,
            AiDataType = AiDataType,
            EntityType = EntityType,
            TranslationType = TranslationType
        };
        inputDeps = scanJob.Schedule(waitScanGroup, inputDeps);

        var reScanJob = new ReScanJob
        {
            CommandBuffer = commandBuffer,
            // 2秒扫描周围
            FilterTime = time - 1,
            AiDataType = AiDataType,
            EntityType = EntityType,
            TranslationType = TranslationType,
            HitDataType = HitDataType
        };
        inputDeps = reScanJob.Schedule(reScanGroup, inputDeps);

        var updateScanJob = new UpdateScanJob
        {
            CommandBuffer = commandBuffer,
            Time = time,
            CastHitsDataType = CastHitsDataType,
            AiDataFromEntity = AiDataFromEntity,
            EntityDataFromEntity = EntityDataFromEntity,
            TranslationFromEntity = TranslationFromEntity
        };
        inputDeps = updateScanJob.Schedule(updateScanGroup, inputDeps);

        startStateGroup.SetSharedComponentFilter(new AiBrainStateData { state = AiBrainState.Wait });
        var startStateJob = new StartStateJob
        {
            CommandBuffer = commandBuffer
        };
        inputDeps = startStateJob.Schedule(startStateGroup, inputDeps);

        judgeGroup.SetSharedComponentFilter(new AiBrainStateData { state = AiBrainState.Judge });
        var execJudgeJob = new ExecJudgeJob
        {
            Time = time,
            // 1秒执行一轮判断
            FilterTime = time - 1,
            CommandBuffer = commandBuffer,
            AiGroupDataFromEntity = AiGroupDataFromEntity,
            AiDataType = AiDataType,
            AiSelectDataType = AiSelectDataType,
            AiLastCommandDataType = AiLastCommandDataType,
            EntityDataType = EntityDataType,
            EntityType = EntityType,
            TranslationType = TranslationType,
            EntityDataFromEntity = EntityDataFromEntity,
            TranslationFromEntity = TranslationFromEntity,
            TargetBufferFromEntity = TargetBufferFromEntity,
            EnemyBufferFromEntity = EnemyBufferFromEntity,
            PartnerBufferFromEntity = PartnerBufferFromEntity
        };
        inputDeps = execJudgeJob.Schedule(judgeGroup, inputDeps);

        runStateGroup.SetSharedComponentFilter(new AiBrainStateData { state = AiBrainState.Run });
        var runStateJob = new RunStateJob
        {
            CommandBuffer = commandBuffer,
            // 2秒执行一次命令
            FilterTime = time - 1,
            TranslationFromEntity = TranslationFromEntity,
            AiGroupDataFromEntity = AiGroupDataFromEntity
        };
        inputDeps = runStateJob.Schedule(runStateGroup, inputDeps);

        CommandBufferSystem.AddJobHandleForProducer(inputDeps);
        inputDeps.Complete();
    }

    public static bool OnCheck(
        out Entity[] resultList,
        ref Entity self,
        ref List<AiPrerequisite> aiPrerequisites,
        EntityData entityData,
        LocalTransform position,
        ref NativeArray<AiTargetBuffer> targetArray,
        ref NativeArray<AiEnemyBuffer> enemyArray,
        ref NativeArray<AiPartnerBuffer> partnerArray,
        ComponentLookup<EntityData> entitiesData,
        ComponentLookup<LocalTransform> positionList)
    {
        IEnumerable<Entity> tmpEntity = null;
        IEnumerable<InfoValue> tmpInfoValue = null;
        resultList = null;
        var e = aiPrerequisites.GetEnumerator();
        while (e.MoveNext())
        {
            var aiPrerequisite = e.Current;
            switch (aiPrerequisite.target)
            {
                case AiPrerequisiteTarget.TARGET:
                    tmpEntity = targetArray.Where(row => entitiesData.HasComponent(row.entity))
                        .Select(info => info.entity);
                    break;
                case AiPrerequisiteTarget.ENEMY:
                    tmpEntity = enemyArray.Where(row => entitiesData.HasComponent(row.entity))
                        .Select(info => info.entity);
                    break;
                case AiPrerequisiteTarget.PARTNER:
                    tmpEntity = partnerArray.Where(row => entitiesData.HasComponent(row.entity))
                        .Select(info => info.entity);
                    break;
                case AiPrerequisiteTarget.SELF:
                    tmpEntity = new Entity[1] { self };
                    break;
                case AiPrerequisiteTarget.MASTER:
                    tmpEntity = new Entity[1] { entityData.master };
                    break;
            }

            var enumerable = tmpEntity.ToArray();
            if (aiPrerequisite.number == 0 && enumerable.Length > 0) return false;
            if (aiPrerequisite.number > 0 && enumerable.Length < aiPrerequisite.number) return false;

            tmpInfoValue = enumerable.Select(entity =>
                GetInfoValue(ref aiPrerequisite, entity, position, entitiesData, positionList));

            switch (aiPrerequisite.filter)
            {
                case AiPrerequisiteCompute.EQUAL:
                    tmpInfoValue = tmpInfoValue.Where(row => row.value == aiPrerequisite.value);
                    break;
                case AiPrerequisiteCompute.GREATER:
                    tmpInfoValue = tmpInfoValue.Where(row => row.value > aiPrerequisite.value);
                    break;
                case AiPrerequisiteCompute.LESS:
                    tmpInfoValue = tmpInfoValue.Where(row => row.value < aiPrerequisite.value);
                    break;
                case AiPrerequisiteCompute.GREATER_EQUAL:
                    tmpInfoValue = tmpInfoValue.Where(row => row.value >= aiPrerequisite.value);
                    break;
                case AiPrerequisiteCompute.LESS_EQUAL:
                    tmpInfoValue = tmpInfoValue.Where(row => row.value <= aiPrerequisite.value);
                    break;
            }

            var infoValues = tmpInfoValue.ToArray();
            if (aiPrerequisite.number == 0 && infoValues.Length > 0) return false;
            if (aiPrerequisite.number > 0 && infoValues.Length < aiPrerequisite.number) return false;

            switch (aiPrerequisite.sort)
            {
                case AiPrerequisiteSort.Asc:
                    tmpInfoValue = infoValues.OrderBy(row => row.value);
                    break;
                case AiPrerequisiteSort.Desc:
                    tmpInfoValue = infoValues.OrderByDescending(row => row.value);
                    break;
            }

            tmpEntity = tmpInfoValue.Select(row => row.entity);
        }

        e.Dispose();
        resultList = tmpEntity.ToArray();
        return true;
    }

    private static InfoValue GetInfoValue(ref AiPrerequisite aiPrerequisite, Entity entity, LocalTransform position,
        ComponentLookup<EntityData> entitiesDataList, ComponentLookup<LocalTransform> positionList)
    {
        var v = InfoValue.Get();
        v.entity = entity;

        switch ((AI_INFO)aiPrerequisite.type)
        {
            case AI_INFO.Base:
                switch ((AI_BASE_SELECT)aiPrerequisite.select)
                {
                    case AI_BASE_SELECT.DISTANCE:
                        if (!positionList.HasComponent(entity))
                            v.value = 0;
                        else
                            v.value = GetDistance(in position, positionList[entity]);
                        break;
                    case AI_BASE_SELECT.NUMBER:
                        v.value = 1;
                        break;
                }

                break;
            case AI_INFO.Attr:
                var targetData = entitiesDataList[entity];
                var life = ObjectService.GetObject<DataObject>(targetData.id);
                if (life == null)
                    v.value = 0;
                else
                    v.value = GetAttr(life, aiPrerequisite.select);
                break;
        }

        return v;
    }

    public static int GetDistance(in LocalTransform source, LocalTransform target)
    {
        return Convert.ToInt32(Vector3.Distance(source.Position, target.Position));
    }

    public static int GetAttr(DataObject life, int select)
    {
        return life.GetAttr().GetWithId(select);
    }

    private static bool Judge(out Entity[] resultList,
        ref Entity entity,
        ref AiCommand aiCommand,
        ref EntityData entityData,
        ref LocalTransform position,
        ref NativeArray<AiTargetBuffer> targetArray, ref NativeArray<AiEnemyBuffer> enemyArray,
        ref NativeArray<AiPartnerBuffer> partnerArray,
        ref ComponentLookup<EntityData> entitiesData, ref ComponentLookup<LocalTransform> positionList)
    {
        var e = aiCommand.prerequisites.GetEnumerator();
        while (e.MoveNext())
        {
            var prerequisiteList = e.Current;
            var result = OnCheck(out var entityList, ref entity, ref prerequisiteList.prerequisites, entityData,
                position, ref targetArray, ref enemyArray, ref partnerArray, entitiesData, positionList);
            if (!result) continue;
            e.Dispose();
            resultList = entityList;
            return true;
        }

        e.Dispose();
        resultList = null;
        return false;
    }

    [BurstCompile]
    private struct WaitGroupJob : IJobChunk
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;

        [WriteOnly] public NativeList<Entity>.ParallelWriter NewGroupList;
        [ReadOnly] public ComponentTypeHandle<AiData> AiDataType;
        [ReadOnly] public ComponentTypeHandle<EntityData> EntityDataType;
        [ReadOnly] public EntityTypeHandle EntityType;
        [ReadOnly] public ComponentTypeHandle<LocalTransform> TranslationType;
        [ReadOnly] public NativeArray<Entity> GroupDataFromGroup;
        [ReadOnly] public ComponentLookup<EntityData> EntityDataFromEntity;
        [ReadOnly] public ComponentLookup<LocalTransform> TranslationFromEntity;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            var aiDataArray = chunk.GetNativeArray(AiDataType);
            var entityDataArray = chunk.GetNativeArray(EntityDataType);
            var entities = chunk.GetNativeArray(EntityType);
            var translationArray = chunk.GetNativeArray(TranslationType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var entity = entities[i];
                var aiData = aiDataArray[i];
                var entityData = entityDataArray[i];
                var t = translationArray[i];
                // 找寻最近的队伍组队，如果没有，自成队伍
                var e = GroupDataFromGroup.GetEnumerator();
                var distance = 0;
                var group = Entity.Null;
                while (e.MoveNext())
                {
                    var entityGroup = e.Current;
                    if (!EntityDataFromEntity[entityGroup].IsPartner(entityData)) continue;
                    var d = GetDistance(in t, TranslationFromEntity[entityGroup]);
                    if (d <= aiData.scanRadius && (d < distance || distance == 0))
                    {
                        distance = d;
                        group = entityGroup;
                    }
                }

                e.Dispose();
                if (group == Entity.Null)
                {
                    // 自我成队伍
                    NewGroupList.AddNoResize(entity);
                }
                else
                {
                    aiData.group = group;
                    CommandBuffer.SetComponent(unfilteredChunkIndex, entity, aiData);
                    CommandBuffer.RemoveComponent<AiWaitGroupData>(unfilteredChunkIndex, entity);
                }
            }
        }
    }

    [BurstCompile]
    private struct NewGroupJob : IJobParallelFor
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;

        [ReadOnly] public NativeArray<Entity> NewGroupList;
        [ReadOnly] public ComponentLookup<AiData> AiDataFromEntity;
        [ReadOnly] public ComponentLookup<EntityData> EntityDataFromEntity;
        [ReadOnly] public ComponentLookup<LocalTransform> TranslationFromEntity;

        public void Execute(int index)
        {
            var entity = NewGroupList[index];
            var entityData = EntityDataFromEntity[entity];
            var aiData = AiDataFromEntity[entity];
            var value = TranslationFromEntity[entity];
            var e = NewGroupList.GetEnumerator();
            var distance = 0;
            var group = Entity.Null;
            while (e.MoveNext())
            {
                var entityGroup = e.Current;
                if (entityGroup == entity) break;
                if (!EntityDataFromEntity[entityGroup].IsPartner(entityData)) continue;
                var d = GetDistance(in value, TranslationFromEntity[entityGroup]);
                if (d <= aiData.scanRadius && (d < distance || distance == 0))
                {
                    distance = d;
                    group = entityGroup;
                }
            }

            e.Dispose();
            if (group == Entity.Null)
            {
                // 自我成队伍
                aiData.group = Entity.Null;
                CommandBuffer.AddComponent<AiGroupData>(index, entity);
            }
            else
            {
                aiData.group = group;
            }

            CommandBuffer.SetComponent(index, entity, aiData);
            CommandBuffer.RemoveComponent<AiWaitGroupData>(index, entity);
        }
    }

    [BurstCompile]
    private struct ScanJob : IJobChunk
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;

        [ReadOnly] public ComponentTypeHandle<AiData> AiDataType;
        [ReadOnly] public EntityTypeHandle EntityType;
        [ReadOnly] public ComponentTypeHandle<LocalTransform> TranslationType;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            var aiDataArray = chunk.GetNativeArray(AiDataType);
            var entities = chunk.GetNativeArray(EntityType);
            var translationArray = chunk.GetNativeArray(TranslationType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var entity = entities[i];
                var aiData = aiDataArray[i];
                var sourcePosition = translationArray[i];
                CommandBuffer.AddComponent(unfilteredChunkIndex, entity, new CastColliderData
                {
                    type = ColliderType.Sphere,
                    Start = sourcePosition.Position,
                    radius = aiData.scanRadius
                });
            }
        }
    }

    [BurstCompile]
    private struct ReScanJob : IJobChunk
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;

        [ReadOnly] public double FilterTime;
        [ReadOnly] public ComponentTypeHandle<AiData> AiDataType;
        [ReadOnly] public EntityTypeHandle EntityType;
        [ReadOnly] public ComponentTypeHandle<LocalTransform> TranslationType;
        [ReadOnly] public ComponentTypeHandle<HitData> HitDataType;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            var aiDataArray = chunk.GetNativeArray(AiDataType);
            var entities = chunk.GetNativeArray(EntityType);
            var translationArray = chunk.GetNativeArray(TranslationType);
            var hitDataArray = chunk.GetNativeArray(HitDataType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var entity = entities[i];
                var hitData = hitDataArray[i];
                if (hitData.time < FilterTime)
                {
                    var aiData = aiDataArray[i];
                    var sourcePosition = translationArray[i];
                    CommandBuffer.AddComponent(unfilteredChunkIndex, entity, new CastColliderData
                    {
                        type = ColliderType.Sphere,
                        Start = sourcePosition.Position,
                        radius = aiData.scanRadius
                    });
                }
            }
        }
    }

    [BurstCompile]
    private partial struct UpdateScanJob : IJobEntity
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;
        [ReadOnly] public double Time;
        [ReadOnly] public BufferLookup<CastHitsData> CastHitsDataType;
        [ReadOnly] public ComponentLookup<AiData> AiDataFromEntity;
        [ReadOnly] public ComponentLookup<EntityData> EntityDataFromEntity;
        [ReadOnly] public ComponentLookup<LocalTransform> TranslationFromEntity;

        public void Execute([EntityIndexInChunk] int index, Entity entity, in AiData ad, in EntityData ed,
            in AiGroupData aiGroupData,
            in LocalTransform t)
        {
            var castHitsDataArray = CastHitsDataType[entity];
            var targetArray = CommandBuffer.AddBuffer<AiTargetBuffer>(index, entity);
            var enemyArray = CommandBuffer.AddBuffer<AiEnemyBuffer>(index, entity);
            var partnerArray = CommandBuffer.AddBuffer<AiPartnerBuffer>(index, entity);
            var e = castHitsDataArray.GetEnumerator();
            var partnerRadius = ad.partnerRadius;
            while (e.MoveNext())
            {
                var row = e.Current;
                if (!EntityDataFromEntity.HasComponent(row.entity)) continue;
                targetArray.Add(new AiTargetBuffer { entity = row.entity });
                var entityData = EntityDataFromEntity[row.entity];
                if (entityData.IsEnemy(ed)) enemyArray.Add(new AiEnemyBuffer { entity = row.entity });

                if (entityData.IsPartner(ed))
                {
                    if (partnerRadius > 0 && partnerRadius > GetDistance(in t, TranslationFromEntity[row.entity]))
                        partnerArray.Add(new AiPartnerBuffer { entity = row.entity });
                    else
                        partnerArray.Add(new AiPartnerBuffer { entity = row.entity });
                }
            }

            e.Dispose();
            CommandBuffer.RemoveComponent<CastHitsData>(index, entity);
            // aiGroupData.time = Time;
            CommandBuffer.SetComponent(index, entity, aiGroupData);

            // 判断周围是否有友方
            var pe = partnerArray.GetEnumerator();
            var i = 0;
            while (pe.MoveNext())
                if (AiDataFromEntity.HasComponent(pe.Current.entity) &&
                    AiDataFromEntity[pe.Current.entity].group == entity)
                    i++;
            pe.Dispose();
            // 周围一半人数的队长，保持
            if (partnerArray.Length == 1 || i >= partnerArray.Length / 2) return;
            CommandBuffer.RemoveComponent<AiGroupData>(index, entity);
            // 变更组
            CommandBuffer.AddComponent<AiWaitGroupData>(index, entity);
        }
    }

    private partial struct StartStateJob : IJobEntity
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;

        public void Execute([EntityIndexInChunk] int index, in Entity entity, in AiData aiData)
        {
            CommandBuffer.AddSharedComponent(index, entity, new AiBrainStateData { state = AiBrainState.Judge });
        }
    }

    private partial struct RunStateJob : IJobEntity
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;

        [ReadOnly] public double FilterTime;
        [ReadOnly] public ComponentLookup<LocalTransform> TranslationFromEntity;
        [ReadOnly] public ComponentLookup<AiGroupData> AiGroupDataFromEntity;

        public void Execute([EntityIndexInChunk] int index, in Entity entity, in AiData aiData,
            in AiLastCommandData last, in LocalTransform t)
        {
            if (last.time > FilterTime) return;
            CommandBuffer.SetSharedComponent(index, entity, new AiBrainStateData { state = AiBrainState.Judge });
            if (aiData.group == Entity.Null) return;
            // 判断距离组长距离
            if (AiGroupDataFromEntity.HasComponent(aiData.group)
                && aiData.scanRadius > GetDistance(in t, TranslationFromEntity[aiData.group])) return;

            // aiData.group = Entity.Null;
            CommandBuffer.SetComponent(index, entity, aiData);
            // 变更组
            CommandBuffer.AddComponent<AiWaitGroupData>(index, entity);
        }
    }

    private struct ExecJudgeJob : IJobChunk
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;
        [ReadOnly] public double Time;
        [ReadOnly] public double FilterTime;
        [ReadOnly] public ComponentTypeHandle<EntityData> EntityDataType;
        [ReadOnly] public ComponentTypeHandle<AiData> AiDataType;
        [ReadOnly] public ComponentTypeHandle<AiSelectData> AiSelectDataType;
        [ReadOnly] public ComponentTypeHandle<AiLastCommandData> AiLastCommandDataType;
        [ReadOnly] public ComponentTypeHandle<LocalTransform> TranslationType;
        [ReadOnly] public EntityTypeHandle EntityType;
        [ReadOnly] public ComponentLookup<EntityData> EntityDataFromEntity;
        [ReadOnly] public ComponentLookup<LocalTransform> TranslationFromEntity;
        [ReadOnly] public BufferLookup<AiTargetBuffer> TargetBufferFromEntity;
        [ReadOnly] public BufferLookup<AiEnemyBuffer> EnemyBufferFromEntity;
        [ReadOnly] public BufferLookup<AiPartnerBuffer> PartnerBufferFromEntity;
        [ReadOnly] public ComponentLookup<AiGroupData> AiGroupDataFromEntity;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            var entityDataArray = chunk.GetNativeArray(EntityDataType);
            var aiDataArray = chunk.GetNativeArray(AiDataType);
            var aiSelectDataArray = chunk.GetNativeArray(AiSelectDataType);
            var aiLastCommandDataArray = chunk.GetNativeArray(AiLastCommandDataType);
            var translationArray = chunk.GetNativeArray(TranslationType);
            var entities = chunk.GetNativeArray(EntityType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var aiSelectData = aiSelectDataArray[i];
                if (aiSelectData.filter && aiSelectData.time > FilterTime) continue;

                var entity = entities[i];
                var aiData = aiDataArray[i];

                var sourceEntity = aiData.group != Entity.Null ? aiData.group : entity;
                if (!AiGroupDataFromEntity.HasComponent(sourceEntity))
                {
                    CommandBuffer.AddComponent<AiWaitGroupData>(unfilteredChunkIndex, entity);
                    CommandBuffer.SetSharedComponent(unfilteredChunkIndex, entity,
                        new AiBrainStateData { state = AiBrainState.Wait });
                    continue;
                }

                if (!TargetBufferFromEntity.HasComponent(sourceEntity)) return;

                var entityData = entityDataArray[i];
                var control = ObjectService.GetObject<Ai>(entityData.id);
                if (control == null) continue;

                var command = control.GetCommand(aiSelectData.commandIndex);
                var last = aiLastCommandDataArray[i];

                Entity[] targetList = null;
                var result = false;
                if (!command.isError)
                {
                    var position = translationArray[i];

                    var targetArray = TargetBufferFromEntity[sourceEntity].AsNativeArray();
                    var enemyArray = EnemyBufferFromEntity[sourceEntity].AsNativeArray();
                    var partnerArray = PartnerBufferFromEntity[sourceEntity].AsNativeArray();
                    result = Judge(out targetList, ref entity, ref command, ref entityData, ref position,
                        ref targetArray, ref enemyArray, ref partnerArray,
                        ref EntityDataFromEntity, ref TranslationFromEntity);
                    if (result)
                        if (command.flag == (int)AI_COMMAND_FLAG.HoldTarget &&
                            aiSelectData.commandIndex == last.commandIndex && targetList.Length > 0)
                            // 如果目标改变，不继续执行
                            if (targetList[0] != last.entity)
                                result = false;
                }

                if (!result)
                {
                    if (aiSelectData.commandIndex == aiData.maxIndex)
                    {
                        CommandBuffer.SetComponent(unfilteredChunkIndex, entity, new AiSelectData
                        {
                            commandIndex = 0,
                            filter = true,
                            time = Time
                        });
                        continue;
                    }

                    var index = aiSelectData.commandIndex + 1;
                    // 自动更新命令
                    CommandBuffer.SetComponent(unfilteredChunkIndex, entity, new AiSelectData
                    {
                        commandIndex = index,
                        filter = false,
                        time = Time
                    });
                    continue;
                }

                if (command.executing)
                {
                    // 直接切换成执行中
                    // 通常是长时间执行的命令，不用打断重来
                    CommandBuffer.SetSharedComponent(unfilteredChunkIndex, entity,
                        new AiBrainStateData { state = AiBrainState.Run });
                    continue;
                }

                CommandBuffer.SetSharedComponent(unfilteredChunkIndex, entity,
                    new AiBrainStateData { state = AiBrainState.WaitRun });
                CommandBuffer.SetComponent(unfilteredChunkIndex, entity, new AiLastCommandData
                {
                    commandIndex = aiSelectData.commandIndex,
                    entity = targetList != null && targetList.Length > 0 ? targetList[0] : Entity.Null,
                    time = Time
                });

                var selectCommand = 0;
                switch ((AI_COMMAND_FLAG)command.flag)
                {
                    case AI_COMMAND_FLAG.HoldTarget:
                        selectCommand = aiSelectData.commandIndex;
                        break;
                    case AI_COMMAND_FLAG.OnlyOnceContinue:
                        selectCommand = aiSelectData.commandIndex + 1;
                        break;
                    case AI_COMMAND_FLAG.OnlyOnceReturn:
                        selectCommand = aiSelectData.commandIndex - 1;
                        break;
                }

                CommandBuffer.SetComponent(unfilteredChunkIndex, entity, new AiSelectData
                {
                    commandIndex = selectCommand,
                    filter = true,
                    time = Time
                });
            }
        }
    }

    private struct InfoValue
    {
        public Entity entity;
        public int value;

        private static readonly Queue<InfoValue> queue = new();

        public static InfoValue Get()
        {
            if (queue.Count > 0)
            {
                return queue.Dequeue();
            }

            var m = new InfoValue();
            return m;
        }

        public static void Release(List<InfoValue> list)
        {
            foreach (var v in list) queue.Enqueue(v);
            list.Clear();
        }
    }
}